import { BaseView } from './../rxjs/cc3/BaseView';

import { _decorator, Component, Node, director, Vec3, v3 } from 'cc';
import { lineIntersection, pointInLine, pointToLineProjection, pointToPlaneProjection } from './math';
const { ccclass, property, executeInEditMode } = _decorator;

declare const CC_EDITOR;

@ccclass('Polygon')
@executeInEditMode
export class Polygon extends BaseView {

    points: Vec3[] = null;

    normal: Vec3 = v3();

    S: Vec3 = v3();

    onLoadView() {
        this.loadPointList();
        if (CC_EDITOR) {
        } else {
            this.node.removeAllChildren();
        }
    }

    loadPointList() {
        this.points = this.node.children.map(_ => _.getWorldPosition());
        Vec3.subtract(this._A, this.points[1], this.points[0]);
        Vec3.subtract(this._B, this.points[this.points.length - 1], this.points[0]);
        Vec3.cross(this.normal, this._A, this._B);
        this.normal.normalize();
        this.S = v3(this.points[0]);
    }

    _intRes = v3();
    _A = v3();
    _B = v3();
    inPolygon(testP: Vec3): boolean {
        let pm01 = v3();
        Vec3.lerp(pm01, this.points[0], this.points[this.points.length - 1], .5);
        let count = 1; // pm01就是第一个交点
        for (let i = 0; i < this.points.length - 1; i++) {
            let P0 = this.points[i];
            let P1 = this.points[i + 1];
            lineIntersection(this._intRes, testP, pm01, P0, P1);
            // 射线内？
            Vec3.subtract(this._A, pm01, testP);
            Vec3.subtract(this._B, this._intRes, testP);
            const dotAB = Vec3.dot(this._A, this._B);
            if (dotAB > 0) {
                // 线段内？
                Vec3.subtract(this._A, P0, this._intRes);
                Vec3.subtract(this._B, P1, this._intRes);
                const dotAB = Vec3.dot(this._A, this._B);
                if (dotAB < 0) {
                    count++;
                }
            }
        }
        return count % 2 != 0; // 只有奇数个交点，那么在里面
    }

    // 找距离最近的交点
    _oriProj = v3();
    _tempEdge = v3();
    findFirstIntersection(out: Vec3, ori: Vec3, tar: Vec3) {
        let distance = Infinity;
        let tarDistance = Vec3.distance(ori, tar);
        for (let i = 0; i < this.points.length; i++) {
            let P0 = this.points[i];
            let P1 = this.points[(i + 1) % this.points.length];

            let inLine = false;
            if (pointInLine(P0, P1, ori)) {
                this.warn('点在线上');
                inLine = true;
                if (this.inPolygon(tar)) {
                    out.set(tar);
                    distance = Infinity;
                    return;
                }
                this._intRes.set(ori);
            } else {
                lineIntersection(this._intRes, ori, tar, P0, P1);
            }

            // 射线内？
            Vec3.subtract(this._A, tar, ori);
            Vec3.subtract(this._B, this._intRes, ori);
            const dotAB = Vec3.dot(this._A, this._B);
            if (inLine || dotAB > 0) {
                let d = Vec3.distance(ori, this._intRes);
                if (inLine) d = 0;
                if (d < tarDistance) {
                    // 线段内？
                    Vec3.subtract(this._A, P0, this._intRes);
                    Vec3.subtract(this._B, P1, this._intRes);
                    const dotAB = Vec3.dot(this._A, this._B);
                    if (inLine || dotAB < 0) {
                        if (d < distance) {
                            Vec3.subtract(this._A, tar, ori);
                            Vec3.subtract(this._tempEdge, P0, P1);
                            this._tempEdge.normalize();
                            let diff = Vec3.dot(this._A, this._tempEdge);
                            this._tempEdge.multiplyScalar(diff);
                            pointToLineProjection(this._oriProj, P0, P1, ori);
                            Vec3.add(out, this._oriProj, this._tempEdge);

                            Vec3.subtract(this._A, out, P0);
                            Vec3.subtract(this._B, out, P1);
                            const dotAB = Vec3.dot(this._A, this._B);
                            if (dotAB > 0) { // 超过端点了
                                if (Vec3.squaredDistance(P0, out) < Vec3.squaredDistance(P1, out)) {
                                    out.set(P0);
                                } else {
                                    out.set(P1);
                                }

                            }

                            distance = d;
                        }
                    }
                }
            }
        }
        return distance;
    }

    projection(out: Vec3, A: Vec3) {
        pointToPlaneProjection(out, A, this.S, this.normal);
    }


}

